﻿#include "InferInfosWin.h"

#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QPlainTextEdit>

#include <QApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QFile>
#include <QMutex>
#include "VisionMainWnd.h"
#include <QCheckBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QFileDialog>
#include <thread>
#include "InspectLL.h"
#include <QThread>
#include <QSpinBox>

#include "Performance.h"
#include <iostream>
#include <chrono>
#include "Login.h"


// 高精度延时函数
void delayTimeMilliSec(int milliseconds)
{
	std::chrono::milliseconds duration(milliseconds);

	// 使当前线程暂停执行指定的时长
	std::this_thread::sleep_for(duration);
}





InferMainWin::InferMainWin(QWidget* parent)
{
	mainWin_ = (VisionMainWnd*)parent;

	initLayout();

	// 读取配置文件信息
	readCfgInfos();

	enableCtrls(false);
}

InferMainWin::~InferMainWin()
{
	
}

void InferMainWin::initLayout()
{
	// 2.导入模型：模型加载，分类文件加载
	QGroupBox* boxLoadModel = new QGroupBox("模型加载", this);
	QLabel* modelInfo = new QLabel("模型路径：");
	QLineEdit* modelPath = new QLineEdit();	
	QPushButton *loadModelBtn = new QPushButton("加载...");
	QLabel* classFileInfo = new QLabel("标签文件:");
	QLineEdit* classesFile = new QLineEdit();
	QPushButton* loadClassesFile = new QPushButton("加载...");
	QGridLayout* gridLayoutLoadmodel = new QGridLayout();
	gridLayoutLoadmodel->addWidget(modelInfo, 0, 0, 1, 1);
	gridLayoutLoadmodel->addWidget(modelPath, 0, 1, 1, 1);
	gridLayoutLoadmodel->addWidget(loadModelBtn, 0, 2, 1, 1);
	gridLayoutLoadmodel->addWidget(classFileInfo, 1, 0, 1, 1);
	gridLayoutLoadmodel->addWidget(classesFile, 1, 1, 1, 1);
	gridLayoutLoadmodel->addWidget(loadClassesFile, 1, 2, 1, 1);
	modelPath_ = modelPath;
	classesPath_ = classesFile;
	boxLoadModel->setLayout(gridLayoutLoadmodel);
	boxLoadModel_ = boxLoadModel;
	boxLoadModel_->hide();

	// 3.视频或者图片加载
	QGroupBox* boxVideoOrPic = new QGroupBox("视频&图片加载", this);
	QLabel* picLabel = new QLabel("文件路径：");
	QLineEdit* picPath = new QLineEdit();
	picPath_ = picPath;
	QPushButton* loadPicBtn = new QPushButton("加载...");
	QHBoxLayout* hBoxLoadPic = new QHBoxLayout();
	hBoxLoadPic->addWidget(picLabel);
	hBoxLoadPic->addWidget(picPath);
	hBoxLoadPic->addWidget(loadPicBtn);

	// 帧率设置与修改:主要针对离线加载视频检测
	QLabel* fpsLab = new QLabel("帧率设置:");
	QSpinBox* fpsSpin = new QSpinBox();
	fpsSpin->setRange(1, 50);
	fpsSpin->setValue(debugFps_);
	QPushButton* inferBtn = new QPushButton("开始检测");
	QPushButton* stopBtn = new QPushButton("停止检测");
	QHBoxLayout* inferBox = new QHBoxLayout();
	inferBox->addWidget(fpsLab);
	inferBox->addWidget(fpsSpin);
	inferBox->addWidget(inferBtn);
	inferBox->addWidget(stopBtn);

	QVBoxLayout* testBox = new QVBoxLayout();
	testBox->addLayout(hBoxLoadPic);
	testBox->addLayout(inferBox);
	boxVideoOrPic->setLayout(testBox);
	boxVideoOrPic->hide();
	boxLoadVideoGroup_ = boxVideoOrPic;


	// 4.输出日志
	QPlainTextEdit* logWin = new QPlainTextEdit(this);
	logWin->setReadOnly(true);
	logWin->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
	logWin->setMinimumHeight(300);

	// 5.参数设置
	QLabel* labConfidence = new QLabel("置信度分数:");
	QLineEdit* edConfidence = new QLineEdit();
	QLabel* labModelScore = new QLabel("得分阈值:");
	QLineEdit* edmodelScore = new QLineEdit();
	QLabel* labNMSScore = new QLabel("非极大值抑制:");
	QLineEdit* edNMScore = new QLineEdit();
	QPushButton* paramsSetBtn = new QPushButton("设置");
	QGridLayout* paramsLayout = new QGridLayout();
	paramsLayout->addWidget(labConfidence, 0, 0);
	paramsLayout->addWidget(edConfidence, 0, 1);
	paramsLayout->addWidget(labModelScore, 1, 0);
	paramsLayout->addWidget(edmodelScore, 1, 1);
	paramsLayout->addWidget(labNMSScore, 2, 0);
	paramsLayout->addWidget(edNMScore, 2, 1);
	paramsLayout->addWidget(paramsSetBtn, 3, 1,1,1);
	QGroupBox* boxParams = new QGroupBox("参数设置", this);
	boxParams->setLayout(paramsLayout);
	// 初始化
	edConfidence->setText("0.25");	// 目标置信度的阈值
	edmodelScore->setText("0.30");	//目标得分的阈值
	edNMScore->setText("0.40");		//非最大抑制的阈值
	modelConfidence_ = edConfidence;
	modelScore_ = edmodelScore;
	modelNMS_ = edNMScore;
	boxInferParams_ = boxParams;
	boxInferParams_->hide();


	QPushButton* clearLogsBtn = new QPushButton("清除日志窗口");

	//QPushButton* stopMacBtn = new QPushButton("停机");
	QPushButton* startMacBtn = new QPushButton("启动");
	QPushButton* clearAllIO = new QPushButton("解除报警&停机");
	QHBoxLayout* macOpBox = new QHBoxLayout();
	//macOpBox->addWidget(stopMacBtn);
	macOpBox->addWidget(startMacBtn);
	macOpBox->addWidget(clearAllIO);
	

	

	QVBoxLayout* mainBox = new QVBoxLayout();
	mainBox->addWidget(boxLoadModel);
	mainBox->addWidget(boxParams);
	
	// 单张测试相关功能按钮隐藏
	
	mainBox->addWidget(boxVideoOrPic);
	mainBox->addLayout(macOpBox);
	mainBox->addWidget(logWin);	
	mainBox->addWidget(clearLogsBtn);
	setLayout(mainBox);

	connect(paramsSetBtn, &QPushButton::clicked, this, &InferMainWin::on_setInferParams);
	connect(inferBtn, &QPushButton::clicked, this, &InferMainWin::on_infer);
	connect(loadClassesFile, &QPushButton::clicked, this, &InferMainWin::on_loadClassess);
	connect(loadModelBtn, SIGNAL(clicked(bool)), this, SLOT(on_loadModel()));
	connect(loadPicBtn, SIGNAL(clicked(bool)), this, SLOT(on_loadPic()));
	connect(clearLogsBtn, SIGNAL(clicked(bool)), this, SLOT(on_clearLogs()));
	connect(startMacBtn, &QPushButton::clicked, this, [=] {
		if (mainWin_ && !mainWin_->isEnableInsp())
		{
			mainWin_->on_startInspect();
		}
		});
	connect(clearAllIO, &QPushButton::clicked, this, [=] {
		//MacOperate::instance()->clearAllIO();
		qInfo() << "手动解除报警";
		});
	connect(stopBtn, SIGNAL(clicked(bool)), this, SLOT(on_stopInfer()));
	connect(fpsSpin, &QSpinBox::valueChanged, [&](int i) {
		//qInfo() << i;
		debugFps_ = i;
		});
}

void InferMainWin::threadInferVideo()
{
	int image_count = 0;
	isProcessing_ = true;

	//double frameRate = 25.0;	// 帧率:可以根据自己需要进行修改，可以放到界面交互或者配置文件
	double timePerFrame = 1000.0 / debugFps_;
	
	Performance timeTest;

	int64 startTime = cv::getTickCount();
	while (isProcessing_)
	{
		timeTest.Reset();
		if (!video_.read(loadImage_))
		{
			break;
		}
		
		mainWin_->grabImage(loadImage_);

		double t = timeTest.GetTimeMilli();
		double sleepTime = timePerFrame - t;

		delayTimeMilliSec(sleepTime);
		
		image_count++;
		int64 endTime = cv::getTickCount();
		t = 1.0 * (endTime - startTime) / cv::getTickFrequency();
		double fps = image_count / t;
		//std::string strFPS = std::to_string(fps) + "fps";
		//cv::putText(loadImage_, strFPS, cv::Point(10, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0),1);
		mainWin_->send_fps(fps);
	}
	qInfo() << "image_count: " << image_count;
	qInfo() << "exit video thread.";
}


void InferMainWin::updateUI(int userType)
{
	if (userType == superUser)
	{
		boxLoadVideoGroup_->show();
		boxLoadModel_->show();
		boxInferParams_->show();
	}
	else if(userType == user)
	{
		boxLoadVideoGroup_->hide();
		boxLoadModel_->hide();
		boxInferParams_->hide();
	}
	
}

void InferMainWin::enableCtrls(bool enable)
{

}

void InferMainWin::writeInfos()
{
	QJsonDocument readAlg;
	QString strPath = QCoreApplication::applicationDirPath() + "/config/alg.json";
	QFile file(strPath);
	QByteArray jsonData;
	if (file.open(QIODevice::ReadOnly)) {
		jsonData = file.readAll().trimmed();
		file.close();
	}
	QJsonParseError err_rpt;
	QJsonDocument jsonDocu = QJsonDocument::fromJson(jsonData, &err_rpt);
	if (jsonDocu.isObject())
	{
		QJsonObject rootObj = jsonDocu.object();
		auto uiInfoObj = rootObj["yolo"].toObject();

		uiInfoObj["model_file"] = modelPath_->text();
		uiInfoObj["classes_label"] = classesPath_->text();

		uiInfoObj["confidence_thresh"] = modelConfidence_->text().toDouble();
		uiInfoObj["score_thresh"] = modelScore_->text().toDouble();
		uiInfoObj["nms_thresh"] = modelNMS_->text().toDouble();

		rootObj["inference"] = uiInfoObj;
		jsonDocu.setObject(rootObj);
	}

	QFile fileSave(strPath);
	if (fileSave.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
		fileSave.write(jsonDocu.toJson());
		fileSave.close();
	}
}


void InferMainWin::readCfgInfos()
{
	QJsonDocument readAlg;
	QString strPath = QCoreApplication::applicationDirPath() + "/config/alg.json";
	QFile file(strPath);
	QByteArray jsonData;
	if (file.open(QIODevice::ReadOnly)) {
		jsonData = file.readAll().trimmed();
		file.close();
	}
	QJsonParseError err_rpt;
	QJsonDocument jsonDocu = QJsonDocument::fromJson(jsonData, &err_rpt);
	double t1=25, t2=0.25, t3=0.45;
	if (jsonDocu.isObject())
	{
		auto uiInfoObj = jsonDocu["inference"];

		t1 = uiInfoObj["modelConfidenceThreshold"].toDouble();
		modelConfidence_->setText(QString::number(t1,'f',3));
		t2 = uiInfoObj["modelScoreThreshold"].toDouble();
		modelScore_->setText(QString::number(t2, 'f', 3));
		t3 = uiInfoObj["modelNMSThresh"].toDouble();
		modelNMS_->setText(QString::number(t3, 'f', 3));
	}

	//mainWin_->GetInfer()->setInferParams(t1, t2, t3);
}

void InferMainWin::on_loadModel()
{
	QString modelPath = QFileDialog::getOpenFileName(this, "加载模型", "",
		"Model Format(*.pt *.pth All(*.*))");
	if (modelPath.isEmpty())
	{
		qInfo() << "加载模型为空";
		return;
	}
	modelPath_->setText(modelPath);
}


void InferMainWin::on_loadPic()
{
	QString picPath = QFileDialog::getOpenFileName(this, "加载图像&视频文件", "",
		"ImageFile(*.jpg *.png *.bmp);;Video(*.mp4 *.avi);;All(*.*))");
	if (picPath.isEmpty())
	{
		qInfo() << "加载图片或者视频为空";
		return;
	}

	// 加载视频
	if (picPath.right(3) == "mp4" || picPath.right(3) == "avi")
	{
		video_.open(picPath.toLocal8Bit().constData());
		if (!video_.isOpened()) {
			std::cerr << "ERROR: 视频无法打开" << std::endl;
			return;
		}
		video_.read(loadImage_);
		// 提前推理一帧图像，分配资源
		setObjType(video_type);
	}
	else
	{
		picPath_->setText(picPath);
		loadImage_ = cv::imread(picPath.toLocal8Bit().constData(), cv::IMREAD_UNCHANGED);
		setObjType(image_type);
	}

	
	if (loadImage_.empty())
	{
		qInfo("图像加载失败");
	}
	else
	{
		QImage img(loadImage_.data, loadImage_.cols, loadImage_.rows, loadImage_.step, QImage::Format_BGR888);
		emit send_Image(img);
		qInfo("加载图像成功");
	}	
	picPath_->setText(picPath);
}

void InferMainWin::showAndHideLog()
{
	
}

void InferMainWin::on_clearLogs()
{
	//_logTextEdit->clear();
}

void InferMainWin::on_infer()
{
	auto obj = getObjType();
	if (obj == image_type)
	{
		mainWin_->process(loadImage_.data, loadImage_.cols, loadImage_.rows, 3);
	}
	else if (obj == video_type)
	{
		mainWin_->on_startInspect();
		mainWin_->startGrab();

		// 启动线程开始视频流推理
		std::thread inferThread(&InferMainWin::threadInferVideo, this);
		inferThread.detach();
	}
}

void InferMainWin::on_stopInfer()
{
	isProcessing_ = false;

	mainWin_->stopGrab();
	QThread::msleep(200);
	mainWin_->on_stopInspect();
}

void InferMainWin::on_setInferParams()
{
	float modelConfidence = modelConfidence_->text().toFloat();
	float modelScore = modelScore_->text().toFloat();
	float modelNMS = modelNMS_->text().toFloat();

	writeInfos();
}

void InferMainWin::on_update_ui(std::string modelPath, std::string labelPath, double confidence, double score, double nms)
{
	modelConfidence_->setText(QString::number(confidence, 10, 2));
	modelScore_->setText(QString::number(score, 10, 2));
	modelNMS_->setText(QString::number(nms, 10, 2));

	modelPath_->setText(modelPath.c_str());
	classesPath_->setText(labelPath.c_str());
}

void InferMainWin::on_stop_machine()
{
}

void InferMainWin::on_start_machine()
{
}


void InferMainWin::on_loadClassess()
{
	QString classesPath = QFileDialog::getOpenFileName(this, "加载标签文件", "",
		"ClassesFile Text(*.txt) All(*.*))");
	if (classesPath.isEmpty())
	{
		qInfo() << "标签文件为空";
		return;
	}
	classesPath_->setText(classesPath);
	//mainWin_->GetInspect()->GetInfer()->loadLabels(classesPath.toLocal8Bit().constData());
}
